#include "filters.h"
#include <algorithm>
#include <vector>
#include <iostream.h>

using namespace std;

int gaussianMatrix3[3][3] = {{1,2,1},
    {2,4,2},
    {1,2,1}
};

int gaussianMatrix5[5][5] = {{1,4,7,4,1},
    {4,16,26,16,4},
    {7,26,41,26,7},
    {4,16,26,16,4},
    {1,4,7,4,1}
};

int sobelMatrixH[3][3] = {{-1,-2,-1},
    {0,0,0},
    {1,2,1}
};

int sobelMatrixV[3][3] = {{-1,0,1},
    {-2,0,2},
    {-1,0,1}
};

int prewittMatrixH[3][3] = {{-1,-1,-1},
    {0,0,0},
    {1,1,1}
};

int prewittMatrixV[3][3] = {{-1,0,1},
    {-1,0,1},
    {-1,0,1}
};



void averageFilter( int matrixArea, pixel** matrixIn, pixel** matrixOut, int height, int width )
{
    switch ( matrixArea )
    {
    case MATRIX_3:
        for ( int i = 0; i < height; i++ )
        {
            for ( int j = 0; j < width; j++ )
            {
                int color = 0;
                for ( int x = -1; x < 2; x++ )
                {
                    for ( int y = -1; y < 2; y++ )
                    {
                        int a = (( i+x < 0 ) || ( i+x >= height )) ? ( a = i-x ) : ( a = i+x );
                        int b = (( j+y < 0 ) || ( i+y >= width )) ? ( b = j-y ) : ( b = j+y );
                        color += matrixIn[a][b].value;
                    }
                }
                matrixOut[i][j].value = color/9;
            }
        }
        //TODO: versao sem for
        //TODO: versao generica
        break;
    case MATRIX_5:
        for ( int i = 0; i < height; i++ )
        {
            for ( int j = 0; j < width; j++ )
            {
                int color = 0;
                for ( int x = -2; x < 3; x++ )
                {
                    for ( int y = -2; y < 3; y++ )
                    {
                        int a = (( i+x < 0 ) || ( i+x >= height )) ? ( a = i-x ) : ( a = i+x );
                        int b = (( j+y < 0 ) || ( i+y >= width )) ? ( b = j-y ) : ( b = j+y );
                        color += matrixIn[a][b].value;
                    }
                }
                matrixOut[i][j].value = color/25;
            }
        }
        break;
    case MATRIX_7:
        for ( int i = 0; i < height; i++ )
        {
            for ( int j = 0; j < width; j++ )
            {
                int color = 0;
                for ( int x = -3; x < 4; x++ )
                {
                    for ( int y = -3; y < 4; y++ )
                    {
                        int a = (( i+x < 0 ) || ( i+x >= height )) ? ( a = i-x ) : ( a = i+x );
                        int b = (( j+y < 0 ) || ( i+y >= width )) ? ( b = j-y ) : ( b = j+y );
                        color += matrixIn[a][b].value;
                    }
                }
                matrixOut[i][j].value = color/49;
            }
        }
        break;
    }
}

void gaussianFilter( int matrixArea, pixel** matrixIn, pixel** matrixOut, int height, int width )
{
    switch ( matrixArea )
    {
    case MATRIX_3:
        for ( int i = 0; i < height; i++ )
        {
            for ( int j = 0; j < width; j++ )
            {
                int color = 0;
                for ( int x = -1; x < 2; x++ )
                {
                    for ( int y = -1; y < 2; y++ )
                    {
                        int a = (( i+x < 0 ) || ( i+x >= height )) ? ( a = i-x ) : ( a = i+x );
                        int b = (( j+y < 0 ) || ( i+y >= width )) ? ( b = j-y ) : ( b = j+y );
                        color += gaussianMatrix3[x+1][y+1] * matrixIn[a][b].value;
                    }
                }
                matrixOut[i][j].value = color/16;
            }
        }
        //TODO: versao sem for
        //TODO: versao generica
        break;
    case MATRIX_5:
        for ( int i = 0; i < height; i++ )
        {
            for ( int j = 0; j < width; j++ )
            {
                int color = 0;
                for ( int x = -2; x < 3; x++ )
                {
                    for ( int y = -2; y < 3; y++ )
                    {
                        int a = (( i+x < 0 ) || ( i+x >= height )) ? ( a = i-x ) : ( a = i+x );
                        int b = (( j+y < 0 ) || ( i+y >= width )) ? ( b = j-y ) : ( b = j+y );
                        color += gaussianMatrix5[x+2][y+2] * matrixIn[a][b].value;
                    }
                }
                matrixOut[i][j].value = color/273;
            }
        }
        break;
    }
}

void medianFilter( int matrixArea, pixel** matrixIn, pixel** matrixOut, int height, int width )
{
    switch ( matrixArea )
    {
    case MATRIX_3:
        for ( int i = 0; i < height; i++ )
        {
            for ( int j = 0; j < width; j++ )
            {
                vector<int> pixels;
                for ( int x = -1; x < 2; x++ )
                {
                    for ( int y = -1; y < 2; y++ )
                    {
                        int a = (( i+x < 0 ) || ( i+x >= height )) ? ( a = i-x ) : ( a = i+x );
                        int b = (( j+y < 0 ) || ( i+y >= width )) ? ( b = j-y ) : ( b = j+y );
                        pixels.push_back((int)matrixIn[a][b].value);
                    }
                }
                sort( pixels.begin(), pixels.end() );
                //cout << (int)pixels.at(4) << " | " << pixels.size();
                matrixOut[i][j].value = (uByte)pixels.at(4);
            }
        }

        //TODO: versao sem for
        //TODO: versao generica
        break;
    case MATRIX_5:
        for ( int i = 0; i < height; i++ )
        {
            for ( int j = 0; j < width; j++ )
            {
                vector<int> pixels;
                for ( int x = -2; x < 3; x++ )
                {
                    for ( int y = -2; y < 3; y++ )
                    {
                        int a = (( i+x < 0 ) || ( i+x >= height )) ? ( a = i-x ) : ( a = i+x );
                        int b = (( j+y < 0 ) || ( i+y >= width )) ? ( b = j-y ) : ( b = j+y );
                        pixels.push_back(matrixIn[a][b].value);
                    }
                }
                sort( pixels.begin(), pixels.end() );
                matrixOut[i][j].value = pixels[12];
            }
        }
        break;
    case MATRIX_7:
        for ( int i = 0; i < height; i++ )
        {
            for ( int j = 0; j < width; j++ )
            {
                vector<int> pixels;
                for ( int x = -3; x < 4; x++ )
                {
                    for ( int y = -3; y < 4; y++ )
                    {
                        int a = (( i+x < 0 ) || ( i+x >= height )) ? ( a = i-x ) : ( a = i+x );
                        int b = (( j+y < 0 ) || ( i+y >= width )) ? ( b = j-y ) : ( b = j+y );
                        pixels.push_back(matrixIn[a][b].value);
                    }
                }
                sort( pixels.begin(), pixels.end() );
                matrixOut[i][j].value = pixels[24];
            }
        }
        break;
    }
}

void sobelFilter( int direction, pixel** matrixIn, pixel** matrixOut, int height, int width )
{
    pixel **matrixAux;
    pixel **matrixPoint = matrixOut;

    if (direction == DHorizontal || direction == DBoth)
    {
        for ( int i = 0; i < height; i++ )
        {
            for ( int j = 0; j < width; j++ )
            {
                int color = 0;
                for ( int x = -1; x < 2; x++ )
                {
                    for ( int y = -1; y < 2; y++ )
                    {
                        int a = (( i+x < 0 ) || ( i+x >= height )) ? ( a = i-x ) : ( a = i+x );
                        int b = (( j+y < 0 ) || ( i+y >= width )) ? ( b = j-y ) : ( b = j+y );
                        color += sobelMatrixH[x+1][y+1] * matrixIn[a][b].value;
                    }
                }
                color = (color < 0)? 0 : ((color > 255)? 255 : color);
                matrixOut[i][j].value = color;
            }
        }
    }

    if (direction == DBoth)
    {
        matrixAux = (pixel **) malloc(height * sizeof(pixel *));
        for (int i = 0; i < height; i++)
        {
            matrixAux[i] = (pixel *) malloc(width * sizeof(pixel));
        }
         matrixPoint = matrixAux;
    }

    if (direction == DVertical || direction == DBoth)
    {
        for ( int i = 0; i < height; i++ )
        {
            for ( int j = 0; j < width; j++ )
            {
                int color = 0;
                for ( int x = -1; x < 2; x++ )
                {
                    for ( int y = -1; y < 2; y++ )
                    {
                        int a = (( i+x < 0 ) || ( i+x >= height )) ? ( a = i-x ) : ( a = i+x );
                        int b = (( j+y < 0 ) || ( i+y >= width )) ? ( b = j-y ) : ( b = j+y );
                        color += sobelMatrixV[x+1][y+1] * matrixIn[a][b].value;
                    }
                }
                color = (color < 0)? 0 : ((color > 255)? 255 : color);
                matrixPoint[i][j].value = color;
            }
        }
    }

    if(direction == DBoth){
        for ( int i = 0; i < height; i++ )
        {
            for ( int j = 0; j < width; j++ )
            {
                matrixOut[i][j].value += matrixPoint[i][j].value;
                matrixOut[i][j].value = (matrixOut[i][j].value > 255)? 255 : matrixOut[i][j].value;
            }
        }
    }


}


void prewittFilter( int direction, pixel** matrixIn, pixel** matrixOut, int height, int width )
{
    pixel **matrixAux;
    pixel **matrixPoint = matrixOut;

    if (direction == DHorizontal || direction == DBoth)
    {
        for ( int i = 0; i < height; i++ )
        {
            for ( int j = 0; j < width; j++ )
            {
                int color = 0;
                for ( int x = -1; x < 2; x++ )
                {
                    for ( int y = -1; y < 2; y++ )
                    {
                        int a = (( i+x < 0 ) || ( i+x >= height )) ? ( a = i-x ) : ( a = i+x );
                        int b = (( j+y < 0 ) || ( i+y >= width )) ? ( b = j-y ) : ( b = j+y );
                        color += prewittMatrixH[x+1][y+1] * matrixIn[a][b].value;
                    }
                }
                color = (color < 0)? 0 : ((color > 255)? 255 : color);
                matrixOut[i][j].value = color;
            }
        }
    }

    if (direction == DBoth)
    {
        matrixAux = (pixel **) malloc(height * sizeof(pixel *));
        for (int i = 0; i < height; i++)
        {
            matrixAux[i] = (pixel *) malloc(width * sizeof(pixel));
        }
         matrixPoint = matrixAux;
    }

    if (direction == DVertical || direction == DBoth)
    {
        for ( int i = 0; i < height; i++ )
        {
            for ( int j = 0; j < width; j++ )
            {
                int color = 0;
                for ( int x = -1; x < 2; x++ )
                {
                    for ( int y = -1; y < 2; y++ )
                    {
                        int a = (( i+x < 0 ) || ( i+x >= height )) ? ( a = i-x ) : ( a = i+x );
                        int b = (( j+y < 0 ) || ( i+y >= width )) ? ( b = j-y ) : ( b = j+y );
                        color += prewittMatrixV[x+1][y+1] * matrixIn[a][b].value;
                    }
                }
                color = (color < 0)? 0 : ((color > 255)? 255 : color);
                matrixPoint[i][j].value = color;
            }
        }
    }

    if(direction == DBoth){
        for ( int i = 0; i < height; i++ )
        {
            for ( int j = 0; j < width; j++ )
            {
                matrixOut[i][j].value += matrixPoint[i][j].value;
                matrixOut[i][j].value = (matrixOut[i][j].value > 255)? 255 : matrixOut[i][j].value;
            }

        }
    }


}
